open List
open Type
open Variable


(* opt1 : compute constant expression
    > replace constant variable by it's value
    > TODO compute expr
*)
let rec opt1 envc ast = 
  match ast with
      [] -> []
    | head::tail -> 
      begin match head with
        Assignment(var, e) -> let v = hd(opt1 envc [e]) in
        begin match var with
          Var(name) -> 
          (* if it's a constant *)
          if (compare (String.uppercase name) name == 0 )
          then begin
            let new_envc = ((name, v)::envc) in
            (opt1 new_envc tail)
          end else ((Assignment(var, v))::(opt1 envc tail))
        | _ -> head::(opt1 envc tail)
        end
      | Binary(e1, op, e2) ->
        let exp1 = hd(opt1 envc [e1]) 
        and exp2 = hd(opt1 envc [e2]) in
          ((Binary(exp1, op, exp2))::(opt1 envc tail))
      | Unary(op, e) -> let exp = hd(opt1 envc [e]) in
                ((Unary(op, exp))::(opt1 envc tail))
      | Var(name) ->   (* if it's a constant *)
          if (compare (String.uppercase name) name == 0 ) 
            then begin
            print_string ("  remove constant `"^name^"`\n");
            let fc = function x -> ((compare (fst x) name) == 0) in
            (try let vc = snd (find fc envc)
              in vc::(opt1 envc tail)
            with NotFound -> error ("  unknown constant :"^name))
          end else head::(opt1 envc tail)
      | Struct_If(if_list) -> Struct_If(List.map (opt1_if envc) if_list)::(opt1 envc tail)
      | Print(plist) -> Print(opt1 envc plist)::(opt1 envc tail)
      | For(var, beg, end_, body) ->
        let new_beg = opt1 envc [beg] and
        new_end = opt1 envc [end_] and
        new_body = opt1 envc body in
        For(var, hd new_beg, hd new_end, new_body)::(opt1 envc tail)
      | While(cond, body) -> 
        let new_c = opt1 envc [cond] and
        new_body = opt1 envc body in
        While(hd new_c, new_body)::(opt1 envc tail)
      | Return(e) -> Return(hd(opt1 envc [e]))::(opt1 envc tail)
      | Function(name, args) ->
        let new_args = opt1 envc args in
        Function(name, new_args)::(opt1 envc tail)
      | Decl_Function(name, args, body) ->
        let new_body = opt1 envc body in
        Decl_Function(name, args, new_body)::(opt1 envc tail)
      | Array_index(name, e) ->
        let new_e = opt1 envc [e] in
        Array_index(name, hd new_e)::(opt1 envc tail)
      | _ -> head::(opt1 envc tail)
      end
      
and opt1_if envc expr = 
  match expr with
      If(cond, body) -> let new_c = hd(opt1 envc [cond]) and
                        new_b = opt1 envc body in
                        If(new_c, rev new_b)
    | Else(body) -> let new_b = opt1 envc body in
                        Else(rev new_b)
;;
